package com.bss.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.faces.model.DataModel;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import com.andy.basics.AbstractMap;
import com.andy.basics.Constants;
import com.andy.basics.Service;
import com.bss.model.Admission;
import com.bss.model.BedBooking;
import com.bss.model.Payment;
import com.bss.model.Receipt;
import com.bss.model.RoomBooking;
import com.bss.service.IdGenerationService;
import com.bss.service.PaymentService;

@Controller
@Scope("request")
public class PaymentController extends com.andy.basics.Controller<Payment> {

	private static final String CLASS = PaymentController.class.getName();

	private static final Logger TRACE = Logger.getLogger(Constants.TRACE_KEY + PaymentController.CLASS);

	private static final Logger DEBUG = Logger.getLogger(Constants.DEBUG_KEY + PaymentController.CLASS);

	private static final Logger AUDIT = Logger.getLogger(Constants.AUDIT_KEY + PaymentController.CLASS);

	private static final Logger ERROR = Logger.getLogger(Constants.ERROR_KEY + PaymentController.CLASS);

	private Date date = new Date();

	private Integer donationAmount;

	private Integer securityDepositAmount;

	private Integer utensilDepositAmount;

	private final AccessFinder accessFinder = new AccessFinder();

	private final VisibilityFinder visibilityFinder = new VisibilityFinder();

	private boolean refundComplete;

	private boolean depositComplete;

	private Integer admissionId = 0;

	private Integer lastId = 0;

	public boolean isRefundComplete() {
		return this.refundComplete;
	}

	@Autowired
	private PaymentService paymentService;

	public PaymentService getPaymentService() {
		return this.paymentService;
	}

	@Autowired
	private IdGenerationService idGenerationService;

	public PaymentController() {
	}

	public DataModel admissions(final Object patientName) {
		PaymentController.DEBUG.debug(patientName);
		if (patientName == null) {
			return null;
		}
		return this.idGenerationService.getCurrentAdmissionData(patientName.toString());
	}

	@Override
	public Payment create() {
		throw new UnsupportedOperationException();
	}

	public Admission getAdmission() {
		if (this.admissionId == 0) {
			this.admissionId = this.getAdmissionId();
			if (this.admissionId != 0) {
				Admission admission = new Admission();
				admission.setId(this.admissionId);
				this.paymentService.populate(admission, this.getDate());
			}
		}
		return this.paymentService.getAdmission();
	}

	public DataModel getBedBookings() {
		return this.paymentService.getBedBookings();
	}

	public DataModel getRoomBookings() {
		return this.paymentService.getRoomBookings();
	}

	@Override
	public Service<Payment> getService() {
		return this.paymentService;
	}

	public void selectAdmission() {
		Admission admission = (Admission) this.idGenerationService.getData().getRowData();
		this.paymentService.populate(admission, this.getDate());
	}

	public void selectBedForPayment() {
		BedBooking bedBooking = (BedBooking) this.paymentService.getBedBookings().getRowData();
		if (!bedBooking.isSelected()) {
			bedBooking.setPayment(null);
		}
	}

	public void selectRoomForPayment() {
		RoomBooking roomBooking = (RoomBooking) this.paymentService.getRoomBookings().getRowData();
		if (!roomBooking.isSelected()) {
			roomBooking.setPayment(null);
		}
	}

	@Override
	public String submit() {
		Receipt receipt = this.createReceipt();
		try {
			this.paymentService.accept(this.paymentService.getAdmission(), receipt);

			this.paymentService.populate(this.getDate());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public String donate() {

		Receipt receipt = this.createReceipt();

		try {
			this.paymentService.donate(this.paymentService.getAdmission(), this.getDonationAmount(), receipt);
			this.paymentService.populate(this.getDate());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public String securityDeposit() {
		Receipt receipt = this.createReceipt();
		try {
			this.depositComplete = this.paymentService.securityDeposit(this.paymentService.getAdmission(),
					this.getSecurityDepositAmount(), receipt);
		} catch (Exception e) {
			PaymentController.ERROR.error("Error while doing security deposit " + this.getSecurityDepositAmount() + " for "
					+ this.getPaymentService().getAdmission(), e);
		}
		return null;
	}

	public String securityDepositRefund() {
		Receipt receipt = this.createReceipt();
		try {
			this.refundComplete = this.paymentService.securityDepositRefund(this.paymentService.getAdmission(),
					this.getSecurityDepositAmount(), receipt);
		} catch (Exception e) {
			PaymentController.ERROR.error("Error while refunding security deposit " + this.getSecurityDepositAmount() + " for "
					+ this.getPaymentService().getAdmission(), e);
		}

		return null;
	}

	public String utensilDeposit() {

		Receipt receipt = this.createReceipt();

		try {
			this.depositComplete = this.paymentService.utensilDeposit(this.paymentService.getAdmission(),
					this.getUtensilDepositAmount(), receipt);
		} catch (Exception e) {
			PaymentController.ERROR.error("Error while doing utensil deposit " + this.getUtensilDepositAmount() + " for "
					+ this.getPaymentService().getAdmission(), e);
		}
		return null;
	}

	protected Receipt createReceipt() {
		Receipt receipt = null;
		if (this.getSelectedReceipt() == null) {
			receipt = new Receipt();
			receipt.setDate(this.getDate());
		} else {
			receipt = this.getSelectedReceipt();
		}
		return receipt;
	}

	public String utensilDepositRefund() {

		Receipt receipt = new Receipt();
		receipt.setDate(this.getDate());
		try {
			this.refundComplete = this.paymentService.utensilDepositRefund(this.paymentService.getAdmission(),
					this.getUtensilDepositAmount(), receipt);
		} catch (Exception e) {
			PaymentController.ERROR.error("Error while refunding utensil deposit " + this.getUtensilDepositAmount() + " for "
					+ this.getPaymentService().getAdmission(), e);
		}
		return null;
	}

	@Override
	public void setDate(final Date date) {
		this.date = date;
	}

	@Override
	public Date getDate() {
		return this.date;
	}

	public DataModel getReceiptsData() {
		return this.paymentService.getReceiptsModel();
	}

	public void selectReceipt() {
		this.paymentService.selectReceipt();
	}

	public List<Payment> getDonations() {
		return this.paymentService.getDonations();
	}

	public void setDonationAmount(final Integer donationAmount) {
		this.donationAmount = donationAmount;
	}

	public Integer getDonationAmount() {
		return this.donationAmount;
	}

	public void setSecurityDepositAmount(final Integer securityDepositAmount) {
		this.securityDepositAmount = securityDepositAmount;
	}

	public Integer getSecurityDepositAmount() {
		return this.securityDepositAmount;
	}

	public List<Payment> getSecurityDeposits() {
		return this.paymentService.getSecurityDeposits();
	}

	public List<Payment> getUtensilDeposits() {
		return this.paymentService.getUtensilDeposits();
	}

	public void setUtensilDepositAmount(final Integer utensilDepositAmount) {
		this.utensilDepositAmount = utensilDepositAmount;
	}

	public Integer getUtensilDepositAmount() {
		return this.utensilDepositAmount;
	}

	public Receipt getSelectedReceipt() {
		return this.paymentService.getSelectedReceipt();
	}

	public String getCurrentProcess() {
		if ((this.getAdmissionId() != 0) && "deposit".equals(this.getOperation())) {
			// TODO: Valid admission id must be checked as well
			return "deposit";
		}
		if ((this.getAdmissionId() != 0) && "refund".equals(this.getOperation())) {
			// TODO: Exit inited must be checked as well
			return "refund";
		}
		return "undefined";
	}

	public Integer getAdmissionId() {
		String admissionId = this.getHttpUtil().getRequestParameter("admissionId");
		return admissionId == null ? 0 : Integer.valueOf(admissionId);
	}

	public String getOperation() {
		return this.getHttpUtil().getRequestParameter("operation");
	}

	public AccessFinder getAccessFinder() {
		return this.accessFinder;
	}

	public VisibilityFinder getVisibilityFinder() {
		return this.visibilityFinder;
	}

	public void setDepositComplete(final boolean depositComplete) {
		this.depositComplete = depositComplete;
	}

	public boolean isDepositComplete() {
		return this.depositComplete;
	}

	private class AccessFinder extends AbstractMap {

		@Override
		public Object get(final Object key) {

			Admission admission = PaymentController.this.getAdmission();

			if (admission == null) {
				return false;
			}

			if (admission.getDateOfExit() != null) {
				return false;
			}

			String op = PaymentController.this.getOperation();

			if (op == null) {
				return true;
			}

			if ("refund".equals(op) && "Donation".equals(key)) {
				return true;
			}

			if ("Utensil Deposit".equals(key)) {
				return admission.getMaterialSet() != null;
			}

			if (admission.getId() != 0) {
				String paymentType = key.toString();
				return PaymentController.this.getPaymentService().isRefundable(paymentType);
			}

			return true;

		}
	}

	private class VisibilityFinder extends AbstractMap {

		@Override
		public Object get(final Object key) {

			PaymentController.DEBUG.debug("Key: " + key);
			String args[] = key.toString().split(",");

			boolean result = false;
			if ("deposit".equals(args[1])) {
				result = PaymentController.this.getPaymentService().allowDeposit(args[0]);
			} else if ("refund".equals(args[1])) {
				result = PaymentController.this.getPaymentService().allowRefund(args[0]);
			}

			PaymentController.DEBUG.debug("Value is " + result);

			return result;

		}
	}

	public Integer calculateTotal(List<Payment> payments) {
		int sum = 0;
		for (Payment payment : payments) {
			sum += payment.getAmount();
		}
		return sum;
	}

	public Integer calculateReceiptTotal(List<Receipt> receipts) {
		int sum = 0;

		for (Receipt receipt : receipts) {
			for (Payment payment : receipt.getPayments()) {
				sum += payment.getAmount();
			}
		}
		return sum;
	}

	public Integer calculateReceiptTotalDeposit(List<Receipt> receipts) {
		int sum = 0;

		for (Receipt receipt : receipts) {
			for (Payment payment : receipt.getPayments()) {
				if (!payment.isRefund()) {
					sum += payment.getAmount();
				}
			}
		}
		return sum;
	}

	public Integer calculateReceiptTotalRefund(List<Receipt> receipts) {
		int sum = 0;

		for (Receipt receipt : receipts) {
			for (Payment payment : receipt.getPayments()) {
				if (payment.isRefund()) {
					sum += payment.getAmount();
				}
			}
		}
		return sum;
	}

	public Integer getUtensilDepositDone(List<Receipt> receipts) {

		int deposit = 0;

		for (Receipt receipt : receipts) {
			for (Payment payment : receipt.getPayments()) {
				if (!payment.isRefund() && payment.getPaymentType().getName().equals("Utensil Deposit")) {
					deposit += payment.getAmount();
				}
			}
		}
		return deposit;
	}

	public Integer getUtensilRefundDone(List<Receipt> receipts) {

		int deposit = 0;

		for (Receipt receipt : receipts) {
			for (Payment payment : receipt.getPayments()) {
				if (payment.isRefund() && payment.getPaymentType().getName().equals("Utensil Deposit") && payment.isRefund()) {
					deposit += payment.getAmount();
				}
			}
		}
		return deposit;
	}

	public Integer getSecurityDepositDone(List<Receipt> receipts) {

		int deposit = 0;

		for (Receipt receipt : receipts) {
			for (Payment payment : receipt.getPayments()) {
				if (!payment.isRefund() && payment.getPaymentType().getName().equals("Security Deposit")) {
					deposit += payment.getAmount();
				}
			}
		}
		return deposit;
	}

	public Integer getSecurityRefundDone(List<Receipt> receipts) {

		int deposit = 0;

		for (Receipt receipt : receipts) {
			for (Payment payment : receipt.getPayments()) {
				if (payment.isRefund() && payment.getPaymentType().getName().equals("Security Deposit")) {
					deposit += payment.getAmount();
				}
			}
		}
		return deposit;
	}

	public void initReceipts() {
		System.out.println("INIT receipts called");
		this.paymentService.initReceipts(this.getAdmission());
	}

	public void completeDeposit() {
		this.paymentService.completeDeposit();
		this.setLastId(this.getAdmission().getId());
		this.paymentService.setAdmission(null);
	}

	public void completeRefund() {
		this.paymentService.completeRefund(this.getDate());
		this.paymentService.setAdmission(null);
	}

	public List<Receipt> getReceipts() {
		return this.paymentService.getReceipts();
	}

	public List<Payment> getDeposits() {
		List<Payment> payments = new ArrayList<Payment>();
		for (Payment payment : this.getAdmission().getPayments()) {
			if (!payment.isRefund()) {
				payments.add(payment);
			}
		}
		return payments;
	}

	public List<Payment> getRefunds() {
		List<Payment> payments = new ArrayList<Payment>();
		for (Payment payment : this.getAdmission().getPayments()) {
			if (payment.isRefund()) {
				payments.add(payment);
			}
		}
		return payments;
	}

	public void setLastId(Integer lastId) {
		this.lastId = lastId;
	}

	public Integer getLastId() {
		return this.lastId;
	}
}
